package rmi.loadtest;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ConnectException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Date;
import org.apache.log4j.Logger;
import socket.domain.Messagepk;

/*
 * Thread for a BiddingClient to simulate login, creating auctions and bidding on auctions
 */
public class TestClientThread  extends Thread  {
	public static Logger log = Logger.getLogger(TestClientThread.class);

	String sentence;
	String linestart=">";
	ArrayList<String> inputLine;
	ObjectOutputStream outToServer = null;
	ObjectInputStream inFromServer = null;
	Socket clientSocket = null;

	java.util.Random random = new java.util.Random();//to choose a random auction from the auctionslist to bid on

	//values from the properties file
	private int auctionsPerMin;
	private int auctionDuration;
	private int updateIntervalSec;
	private int bidsPerMin;

	private Date startTime; //startTime from the LoadTest to calculate the bidding prices
	private  boolean stop=false;//if the LoadTest terminates this flag will be set and the Thread will stop running
	private static int user =1;//static variable to name the testusers

	private ArrayList<Integer> activeAuctions = null;
	private int index = 0;

	public TestClientThread(String tcpport, String host,int auctionsPerMin,int auctionDuration,int updateIntervalSec,int bidsPerMin, Date startTime) throws ConnectException,UnknownHostException,IOException {
		this.auctionsPerMin= auctionsPerMin;
		this.auctionDuration = auctionDuration;
		this.updateIntervalSec = updateIntervalSec;
		this.bidsPerMin=bidsPerMin;
		this.startTime = startTime;

		int tcp = Integer.parseInt(tcpport);
		//creating socket to communicate with the auction server
		clientSocket = new Socket(host,tcp);

		try {
			outToServer = new ObjectOutputStream(clientSocket.getOutputStream());
			inFromServer = new ObjectInputStream(clientSocket.getInputStream());			
			outToServer.writeObject(new Messagepk((String.valueOf(100)))); 
		} catch (IOException e1) {			
			e1.printStackTrace();
			cleanUp();
		}

		//login Testuser
		sentence = "!login user"+user;
		user++; 

	}

	public void run() {
		int sec = 0;
		try{
			process();//sends the command to the server 
			//as long as the LoadTest is not terminated
			while(!isStop()){


				//if the current seconds modulo auctions in one minute is 0, an auction will be created 
				if(auctionsPerMin>0 && auctionsPerMin <=60){
					if((sec%(60/auctionsPerMin))==0){
						LoadTest.auction++;
						sentence = "!create "+this.auctionDuration+" Test auction "+LoadTest.auction; 				
						process();				
					}
				}
				else if(auctionsPerMin>60){
					for(int i = 0; i<auctionsPerMin/60; i++){
						LoadTest.auction++;
						sentence = "!create "+this.auctionDuration+" Test auction "+LoadTest.auction; 				
						process();	
					}
				}

				//if the current seconds modulo update interval is 0 the list with the active auctions will be refreshed
				if(updateIntervalSec>0){
					if((sec%(updateIntervalSec))==0){
						sentence = "!list";
						process();				
					}
				}
				//a random auction will be choosen from the acitve auctions list and a bid will be placed on it with the amount of seconds and millisecons which have passed since the loadtest was started

				if(bidsPerMin>0 && bidsPerMin <=60){
					if((sec%(60/bidsPerMin))==0){	
						if (activeAuctions!=null){					
							if (activeAuctions.size()>0){
								index = random.nextInt(activeAuctions.size()); 
								Date current = new Date();
								double bidPrice = (Double.valueOf(current.getTime())-Double.valueOf(startTime.getTime()))/1000;						
								sentence = "!bid "+activeAuctions.get(index)+" "+bidPrice;
								process();
							}
							else
								log.debug("no available auctions: index = 0");
						}
						else
							log.debug("no available auctions: activeAuctions is null");				
					}
				}
				else if(bidsPerMin>60){
					for(int i = 0; i<bidsPerMin/60; i++){
						if (activeAuctions!=null){					
							if (activeAuctions.size()>0){
								index = random.nextInt(activeAuctions.size()); 
								Date current = new Date();
								double bidPrice = (Double.valueOf(current.getTime())-Double.valueOf(startTime.getTime()))/1000;						
								sentence = "!bid "+activeAuctions.get(index)+" "+bidPrice;
								process();
							}
							else
								log.debug("no available auctions: index = 0");
						}
						else
							log.debug("no available auctions: activeAuctions is null");	
					}
				}

				//waits one second so the values from the loadtest.properties can be considered without creating new Threads for each command
				synchronized(this){
					this.wait(1000);			
					sec++;
				}		
				log.debug("sec: "+sec);	
			}	
			cleanUp();
		} catch (IOException e) {			
			log.info("TestClient disconnected ");		
			cleanUp();			
		}
		catch (InterruptedException e1) {
			log.info("TestClient interrupted ");
			e1.printStackTrace();
		}
	}

	public void process() throws IOException{

		Messagepk incomming = new Messagepk();
		try{
			//send object to server
			outToServer.writeObject(new Messagepk(sentence));			
			//wait form answer from server
			incomming = (Messagepk)(inFromServer.readObject());		
		}
		catch (ClassNotFoundException e) {			
		}
		//ArrayList of the message
		inputLine = incomming.getMessage();

		//list all active auctions
		if(sentence.equals("!list"))
			activeAuctions = new ArrayList<Integer>();

		//read ArrayList with messges
		for(int i =0;i<inputLine.size();i++){
			linestart = incomming.getUserLine();
			//username=incomming.getUsername();
			log.debug(inputLine.get(i));	

			if(sentence.equals("!list")){
				//get auction number
				String[] s =(inputLine.get(i)).split("\\.");
				//if auction is available add to the active auctions list
				if(s.length>0)
					activeAuctions.add(Integer.valueOf(s[0]));
			}
		}
	}

	public void cleanUp(){
		//closing open connection and stream
		try{outToServer.close();}
		catch(SocketException e){/*lost connection to the host*/} 
		catch (IOException e) {
			e.printStackTrace();
		}
		try{inFromServer.close();}
		catch(SocketException e){/*lost connection to the host*/} 
		catch (IOException e) {
			e.printStackTrace();
		}
		try{clientSocket.close();}
		catch(SocketException e){/*lost connection to the host*/} 
		catch (IOException e) {
			e.printStackTrace();
		}

	}

	public  boolean isStop() {
		return stop;
	}

	public  void setStop(boolean stop) {
		this.stop = stop;
	}
}

